package com.newfiber.common.core.web.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.newfiber.common.core.context.SecurityContextHolder;
import com.newfiber.common.core.enums.EDelFag;
import com.newfiber.common.core.utils.DateUtils;
import com.newfiber.common.core.utils.PageUtils;
import com.newfiber.common.core.web.domain.BaseEntity;
import com.newfiber.common.core.web.page.PageResult;
import com.newfiber.common.core.web.page.PageSupport;
import com.newfiber.common.core.web.support.QueryDeletedInterceptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.NotBlank;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

/**
 *
 */
@Validated
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> {

    private final LambdaWrapper lambdaWrapper = new LambdaWrapper();

    @Override
    public boolean save(T entity) {
        this.resolveEntity(entity);
        return super.save(entity);
    }

    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.saveBatch(entityList, batchSize);
    }

    @Override
    public boolean updateById(T entity) {
        this.resolveEntity(entity);
        return super.updateById(entity);
    }

    @Override
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.updateBatchById(entityList, batchSize);
    }

    @Override
    public boolean saveOrUpdate(T entity) {
        if (entity.getId() == null) {
            return this.save(entity);
        } else {
            return this.updateById(entity);
        }
    }

    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.saveOrUpdateBatch(entityList, batchSize);
    }

    public String generatorNumber(String prefix, NumberFormat numberFormat) {
	    return generatorNumber(prefix, numberFormat, 3);
    }

    public String generatorNumber(String prefix, NumberFormat numberFormat, Integer fillLength) {

        long count;
        Date now = new Date();
        QueryWrapper<T> queryWrapper;

        switch (numberFormat) {
            case DateSerialNumber:
                queryWrapper = new QueryWrapper<T>().eq("DATE_FORMAT(create_time, '%Y-%m-%d')", DateUtil.format(now, "yyyy-MM-dd"))
	                .last(QueryDeletedInterceptor.QUERY_DELETED_DATA);
                count = count(queryWrapper);
                return prefix.concat(DateUtil.format(now, "yyyyMMdd")).concat(StrUtil.fillBefore(String.valueOf(count + 1), '0', fillLength));

            case MmDdSerialNumber:
                queryWrapper = new QueryWrapper<T>().eq("DATE_FORMAT(create_time, '%Y-%m-%d')", DateUtil.format(now, "yyyy-MM-dd"))
	                .last(QueryDeletedInterceptor.QUERY_DELETED_DATA);
                count = count(queryWrapper);
                return prefix.concat(DateUtil.format(now, "MMdd")).concat(StrUtil.fillBefore(String.valueOf(count + 1), '0', fillLength));

            default:
                return "";
        }
    }

    public <R extends SFunction<T, ?>> long count(R column, Object value) {
        LambdaQueryWrapper<T> queryWrapper = Wrappers.<T>query().lambda().eq(column, value);
        return count(queryWrapper);
    }

    public <R extends SFunction<T, ?>> boolean duplicateCheck(R column, Object value) {
        if (ObjectUtils.isEmpty(value)) {
            return false;
        }
        long sameCount = count(column, value);
        return sameCount > 0;
    }

    public <R extends SFunction<T, ?>> boolean duplicateCheck(Long id, R column, Object value) {
        if (ObjectUtils.isEmpty(value)) {
            return false;
        }

        T data = getById(id);
        return duplicateCheck(data, column, value);
    }

    public <R extends SFunction<T, ?>> boolean duplicateCheck(T data, R column, Object value) {
        String columnName = lambdaWrapper.columnToString(column);
        if (columnName.contains("_")) {
            columnName = StrUtil.toCamelCase(columnName);
        }
        Object oldValue = BeanUtil.getProperty(data, columnName);
        if (ObjectUtil.isNotEmpty(value) && ObjectUtil.isNotEmpty(oldValue) && !oldValue.equals(value)) {
            return duplicateCheck(column, value);
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean deleteLogic(@NotBlank String ids) {
        List<T> list = new ArrayList<>();
        Arrays.asList(ids.split(",")).forEach(id -> {
            T entity = BeanUtils.instantiateClass(currentModelClass());
            entity.setId(Long.parseLong(id));
            entity.setDelFlag(EDelFag.YES.getCode());
            entity.setUpdateBy(SecurityContextHolder.getNickName());
            entity.setUpdateTime(new Date());
            list.add(entity);
        });
        return super.updateBatchById(list) && removeByIds(Arrays.asList(ids.split(",")));
    }

    @SneakyThrows
    private void resolveEntity(T entity) {
        Date now = DateUtils.getNowDate();
        if (entity.getId() == null) {
            // 处理新增逻辑
            if(entity.getCreateBy() == null){
                entity.setCreateBy(SecurityContextHolder.getUserName());
            }
            entity.setCreateTime(now);
            entity.setUpdateBy(SecurityContextHolder.getNickName());
            entity.setUpdateTime(now);
        } else {
            // 处理修改逻辑
            entity.setUpdateBy(SecurityContextHolder.getNickName());
            entity.setUpdateTime(now);
        }
        entity.setDelFlag(EDelFag.NO.getCode());
    }

	/**
	 * 设置请求分页数据
	 */
	protected void startPage(){
		PageUtils.startPage();
	}

	/**
	 * 响应请求分页数据
	 */
	protected PageResult<List<T>> pageResult(List<T> list){
		return PageSupport.pageResult(list);
	}

    @SuppressWarnings({"unchecked", "rawtypes"})
    static class LambdaWrapper extends AbstractLambdaWrapper {

        @Override
        protected String columnToString(SFunction column) {
            return super.columnToString(column);
        }

        @Override
        protected AbstractWrapper instance() {
            return new LambdaWrapper();
        }
    }

    public enum NumberFormat {
        /**
         * 日期流水号 ：20230101001
         */
        DateSerialNumber,

        /**
         * 日期流水号 ：0101001
         */
        MmDdSerialNumber;
    }
}
